package org.sapling.data.config;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;
import org.sapling.common.utils.encryption.Base64Util;
import org.sapling.data.exp.DataTransException;
import org.sapling.data.utils.DataTableFieldTypeTranslateUtils;
import org.sapling.data.utils.DataTableFieldTypeTranslateUtils.FieldType;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

import com.zaxxer.hikari.HikariDataSource;


/**
 * 
 * @ClassName: DataSourceConfig  
 * @Description: 数据源配置类
 * @author DcfZero 
 * @date 2020年5月31日  
 *
 */
@Configuration
public class DataSourceConfig{
	private static final Logger log = org.slf4j.LoggerFactory.getLogger(DataSourceConfig.class);

	@Value("${spring.datasource.platform}")
	private String platform;
	@Value("${spring.datasource.url}")
	private String url;
	@Value("${spring.datasource.username}")
	private String username;
	@Value("${spring.datasource.password}")
	private String password;
	@Value("${spring.datasource.driver-class-name}")
	private String driverClassName;

	@Value("${spring.datasource.hikari.maximum-pool-size:10}")
	private int maxPoolSize;

	@Value("${spring.datasource.hikari.minimum-idle:1}")
	private int minIdle;

	@Value("${spring.datasource.hikari.max-lifetime:600000}")
	private long maxLifetime;

	@Autowired
	private JdbcTemplate jdbcTemplate;
	@Autowired
	private DataTableFieldTypeTranslateUtils utils;

	/**
	 * 
	 * @Title: dataSourceRepository  
	 * @Description: 初始化数据源
	 * @param @return  参数  
	 * @return HashMap<String,JdbcTemplate>  返回类型  
	 * @throws
	 */
	@Bean
	public HashMap<String, JdbcTemplate> dataSourceRepository(){
		long start = System.currentTimeMillis();
		log.info("开始创建数据源");
		HashMap<String,JdbcTemplate> jdbcMap = new HashMap<String,JdbcTemplate>();
		Set<String> dataSourceNames = new HashSet<String>();
		//查看当前配置数据源内是否已有datareg表
		boolean hasTable = this.jdbcTemplate.query("SELECT table_name FROM information_schema.TABLES WHERE table_name = 'sys_config_connection'",
				(rs) -> {
					if(!rs.next()) {
						return false;
					}
					return true;
				});
		long end = System.currentTimeMillis();
		if(!hasTable) {
			log.info("没有找到sys_config_connection表，正在创建");
			utils.init(platform);
			try {
				this.jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS sys_config_connection ("+
						"i_id " + utils.translateFieldType(FieldType.INT, true, true) +
						",i_datasource_type " + utils.translateFieldType(FieldType.INT, true) +
						",c_datasource_connectId " + utils.translateFieldType(FieldType.STRING, true) +
						",c_datasource_connectUrl " + utils.translateFieldType(FieldType.STRING, true) +
						",c_datasource_aliasName " + utils.translateFieldType(FieldType.STRING) +
						" ,PRIMARY KEY (i_id))ENGINE=InnoDB DEFAULT CHARSET = utf8 COLLATE = utf8_unicode_ci");
			} catch (DataAccessException | DataTransException e) {
				e.printStackTrace();
			}
			log.info("创建sys_config_connection表成功");
		}else {
			String dataSourcesSql = "SELECT * FROM sys_config_connection";
			this.jdbcTemplate.query(dataSourcesSql, (rs) -> {				
				try {
					DataSource dataSource = this.builderDataSource(rs.getInt("i_datasource_type"), rs.getString("c_datasource_connectUrl"));
					jdbcMap.put(rs.getString("c_datasource_connectId"), (dataSource == null) ? this.jdbcTemplate : new JdbcTemplate(dataSource));
					dataSourceNames.add(rs.getString("c_datasource_aliasName"));
				} catch (Exception e) {
					e.printStackTrace();
				}
			});
		}
		log.info(String.format("创建了[%s]{%s}个数据源，共耗时%s毫秒", StringUtils.join(dataSourceNames,","),jdbcMap.size(), end - start));
		return jdbcMap;
	}

	/**
	 * 根据数据源类型和连接字符串构建数据源
	 * @throws Exception 
	 * @Title: builderDataSource  
	 * @param datasourceType 数据源类型
	 * @param dataSourceConnectUrl 数据源连接参数
	 * @return DataSource  
	 * @throws
	 */
	private DataSource builderDataSource(Integer datasourceType,String dataSourceConnectUrl) throws Exception {
		String server = "";
		String port = "";
		String database = "";
		String userName = "";
		String password = "";
		String schema = "";
		String driver = "";		
		for(String connectItem : StringUtils.split(dataSourceConnectUrl, ";")) {
			String[] itemValue = StringUtils.split(connectItem,"=");
			if("server".equalsIgnoreCase(itemValue[0])) {
				server = itemValue[1];
			}else if("port".equalsIgnoreCase(itemValue[0])) {
				port = itemValue[1];
			}else if("database".equalsIgnoreCase(itemValue[0])) {
				database = itemValue[1];
			}else if("userName".equalsIgnoreCase(itemValue[0])) {
				userName = itemValue[1];
			}else if("password".equalsIgnoreCase(itemValue[0])) {
				password = Base64Util.decode(itemValue[1]);
			}else if("schema".equalsIgnoreCase(itemValue[0])) {
				schema =  itemValue[1];
			}else if("driver".equalsIgnoreCase(itemValue[0])) {
				driver =  itemValue[1];
			}
		}

		StringBuilder url = new StringBuilder();
		switch (datasourceType) {
		case 1:
			driver = "org.mariadb.jdbc.Driver";
			url.append("jdbc:mariadb://").append(server).append(":")
			.append(StringUtils.isBlank(port) ? "3306" : port).append("/").append(database)
			.append("?autoReconnect=true");
			break;
		case 2:
			driver = "com.mysql.jdbc.Driver";
			url.append("jdbc:mysql://").append(server).append(":")
			.append(StringUtils.isBlank(port) ? "3306" : port).append("/").append(database)
			.append("?autoReconnect=true");
			break;
		case 3:
			driver = "oracle.jdbc.driver.OracleDriver";
			url.append("jdbc:oracle:thin:@").append(server).append(":")
			.append(StringUtils.isBlank(port) ? "1521:" : port).append(":").append(database);
			break;
		case 4:
			driver = "org.postgresql.Driver";
			url.append("jdbc:postgresql://").append(server).append(":").append(StringUtils.isBlank(port) ? "5432" : port)
			.append("/").append(database).append("?useSSL=false&currentSchema=").append(schema);
			break;
		case 5:	
			driver = "com.oscar.Driver";
			url.append("jdbc:oscar://").append(server).append(":")
			.append(StringUtils.isBlank(port) ? "2003" : port).append("/").append(database);
			break;
		}
		HikariDataSource dataSource = new HikariDataSource();
		dataSource.setJdbcUrl(url.toString());
		dataSource.setUsername(userName);
		dataSource.setPassword(password);
		dataSource.setDriverClassName(driver);
		dataSource.setMaximumPoolSize(this.maxPoolSize);
		dataSource.setMinimumIdle(this.minIdle);
		dataSource.setMaxLifetime(this.maxLifetime);

		return dataSource;
	}
}